use std::{collections::HashMap, sync::{Arc, LazyLock, Mutex}};
use crate::engine::njm_model::NjmModel;
use getset::{CloneGetters, CopyGetters, Getters, MutGetters, Setters, WithSetters};


#[derive(Getters, Setters, WithSetters, MutGetters, CopyGetters, CloneGetters)]
pub struct TransfromComponent {
    #[getset(get = "pub", set = "pub")]
    pub translation: cgmath::Vector3<f32>,
    
    #[getset(get = "pub", set = "pub")]
    pub scale: cgmath::Vector3<f32>,

    #[getset(get = "pub", set = "pub")]
    pub rotation: cgmath::Vector3<f32>,
}

impl Default for TransfromComponent {
    fn default() -> Self {
        return Self {
            translation: [0.0, 0.0, 0.0].into(),
            scale: [1.0, 1.0, 1.0].into(),
            rotation: [0.0, 0.0, 0.0].into(),
        };
    }
}

impl TransfromComponent {
    pub fn mat4(&self) -> cgmath::Matrix4<f32> {
        let c3: f32 = self.rotation.z.cos();
        let s3: f32 = self.rotation.z.sin();
        let c2: f32 = self.rotation.x.cos();
        let s2: f32 = self.rotation.x.sin();
        let c1: f32 = self.rotation.y.cos();
        let s1: f32 = self.rotation.y.sin();

        return cgmath::Matrix4::<f32> {
            x: cgmath::Vector4 {
                x: self.scale.x * (c1 * c3 + s1 * s2 * s3),
                y: self.scale.x * (c2 * s3),
                z: self.scale.x * (c1 * s2 * s3 - c3 * s1),
                w: 0.0,
            },
            y: cgmath::Vector4 {
                x: self.scale.y * (c3 * s1 * s2 - c1 * s3),
                y: self.scale.y * (c2 * c3),
                z: self.scale.y * (c1 * c3 * s2 + s1 * s3),
                w: 0.0,
            },
            z: cgmath::Vector4 {
                x: self.scale.z * (c2 * s1),
                y: self.scale.z * (-s2),
                z: self.scale.z * (c1 * c2),
                w: 0.0,
            },
            w: cgmath::Vector4 {
                x: self.translation.x, 
                y: self.translation.y, 
                z: self.translation.z, 
                w: 1.0,
            },
        };
    }

    pub fn normal_mat(&self) -> cgmath::Matrix4<f32> {
        let c3: f32 = self.rotation.z.cos();
        let s3: f32 = self.rotation.z.sin();
        let c2: f32 = self.rotation.x.cos();
        let s2: f32 = self.rotation.x.sin();
        let c1: f32 = self.rotation.y.cos();
        let s1: f32 = self.rotation.y.sin();
        let scale_inv = 1.0 / self.scale;
        
        return cgmath::Matrix4::<f32> {
            x: cgmath::Vector4 {
                x: scale_inv.x * (c2 * s3),
                y: scale_inv.x * (c1 * s2 * s3 - c3 * s1),
                z: scale_inv.x * (c1 * c3 + s1 * s2 * s3),
                w: 0.0,
            },
            y: cgmath::Vector4 {
                x: scale_inv.y * (c3 * s1 * s2 - c1 * s3),
                y: scale_inv.y * (c2 * c3),
                z: scale_inv.y * (c1 * c3 * s2 + s1 * s3),
                w: 0.0,
            },
            z: cgmath::Vector4 {
                x: scale_inv.z * (c2 * s1),
                y: scale_inv.z * (-s2),
                z: scale_inv.z * (c1 * c2),
                w: 0.0,
            },
            w: cgmath::Vector4 { x: 0.0, y: 0.0, z: 0.0, w: 1.0 },
        };
    }
}

// 
pub(crate) type id_t = u32;
pub(crate) type Map = HashMap<id_t, NjmGameObj>;

#[derive(Getters, Setters, WithSetters, MutGetters, CopyGetters, CloneGetters)]
pub struct NjmGameObj {
    #[getset(get = "pub")]
    id: id_t,
    
    #[getset(get = "pub", set = "pub")]
    pub color: cgmath::Vector3<f32>,

    #[getset(get = "pub", set = "pub")]
    pub transform: TransfromComponent,

    #[getset(get = "pub", set = "pub")]
    pub model: Option<Arc<NjmModel>>,
}

static CURRENT_ID: LazyLock<Mutex<id_t>> = LazyLock::new(|| Mutex::new(0));

impl NjmGameObj {
    const DEAULT_OBJ_COLOR: cgmath::Vector3<f32> = cgmath::Vector3::<f32> {x: 0.0, y: 0.0, z: 0.0};
    
    pub fn create_game_obj() -> Self {
        let mut current_id_ref = unsafe { CURRENT_ID.lock().unwrap() };
        let current_id = *current_id_ref;
        *current_id_ref += 1;
        return Self::new(current_id);
    }
    fn new(id: id_t) -> Self {
        return Self {
            id: id,
            color: Self::DEAULT_OBJ_COLOR,
            transform: TransfromComponent::default(),
            model: None,
        };
    }
}
